using System;
using System.Collections.Generic;
using System.Text;

namespace GPSCore
{
    /// <summary>
    /// 3d Direction (Azimuth and Elevation)
    /// </summary>
    public class Direction : IEquatable<Direction>
    {
        #region members

        Azimuth _azimuth;
        Elevation _elevation;

        #endregion

        #region constructors

        /// <summary>
        /// Build an unknown Direction object
        /// </summary>
        public Direction()
        {
             _azimuth = new Azimuth(0.0);
            _elevation = new Elevation(0.0);
        }


        /// <summary>
        /// Build a qulified Direction (no 3d direction)
        /// </summary>
        /// <param name="azimuth"></param>
        public Direction(double azimuth)
        {
            _azimuth = new Azimuth(azimuth);
            _elevation = new Elevation(0.0);
        }


        /// <summary>
        /// Build a qualified Direction object (no 3d direction)
        /// </summary>
        /// <param name="azimuth"></param>        
        public Direction(Azimuth azimuth)
        {
            _azimuth = azimuth;
            _elevation = new Elevation(0.0);
        }

        /// <summary>
        /// Build a fully qualified Direction object
        /// </summary>
        /// <param name="azimuth"></param>
        /// <param name="elevation"></param>
        public Direction(double azimuth, double elevation)
        {
            _azimuth = new Azimuth(azimuth);
            _elevation = new Elevation(elevation);
        }

        /// <summary>
        /// Build a fully qualified Direction object
        /// </summary>
        /// <param name="azimuth"></param>
        /// <param name="elevation"></param>
        public Direction(Azimuth azimuth, Elevation elevation)
        {
            _azimuth = azimuth;
            _elevation = elevation;
        }


        /// <summary>
        /// Build a Direction basing on two Positions
        /// </summary>
        /// <param name="P1"></param>
        /// <param name="P2"></param>
        public Direction(Position P1, Position P2)
        {
            /*
            // convert latitude and longitude to radians
            double lat1 = DegreesToRadians((double)pt1.lat);
            double lon1 = DegreesToRadians((double)pt1.lon);
            double lat2 = DegreesToRadians((double)pt2.lat);
            double lon2 = DegreesToRadians((double)pt2.lon);

            // compute latitude and longitude differences
            double dlat = lat2 - lat1;
            double dlon = lon2 - lon1;

            double distanceNorth = dlat;
            double distanceEast = dlon * Math.Cos(lat1);

            // compute the distance in radians
            dist = Math.Sqrt(distanceNorth * distanceNorth + distanceEast * distanceEast);

            // and convert the radians to meters
            dist = RadiansToMeters(dist);

            // add the elevation difference to the calculation
            double dele = (double)pt2.ele - (double)pt1.ele;
            dist = Math.Sqrt(dist * dist + dele * dele);

            // compute the course
            course = Math.Atan2(distanceEast, distanceNorth) % (2 * Math.PI);
            course = RadiansToDegrees(course);
            if (course < 0)
                course += 360;
            */

            // convert latitude and longitude to radians
            double dLat1InRad = GPSConvertManager.DegreesToRadians(P1.Latitude.DecimalDegrees);
            double dLong1InRad = GPSConvertManager.DegreesToRadians(P1.Longitude.DecimalDegrees);
            double dLat2InRad = GPSConvertManager.DegreesToRadians(P2.Latitude.DecimalDegrees);
            double dLong2InRad = GPSConvertManager.DegreesToRadians(P2.Longitude.DecimalDegrees);
            
            // compute latitude and longitude differences
            double dLongitude = dLong2InRad - dLong1InRad;
            double dLatitude = dLat2InRad - dLat1InRad;
            
            // Compute direction 
            double distanceNorthInRad = dLatitude;
            double distanceEastInRad = dLongitude * Math.Cos(dLat1InRad);


            // compute the course
            double course = Math.Atan2(distanceEastInRad, distanceNorthInRad) % (2 * Math.PI);
            course = GPSConvertManager.RadiansToDegrees(course);

            if (course < 0)
                course += 360;

            _azimuth = new Azimuth ( course );
       
            //calculate also the elevation for the two points...

            // Distance (basing on the great circle distance
            double onEarthDistance = GPSConvertManager.EarthRadiusKms * P1.GreatCircleDistanceTo(P2) * 1000.0; //* 1000 to convert in meters

            if (P2.Altitude.HasValue & P1.Altitude.HasValue)
            {
                // add the elevation difference to the calculation
                double elevationDifferenceMeters = P2.Altitude.Value - P1.Altitude.Value;

                //raw and approsimative forumla (simply trigonometry)
                _elevation = new Elevation(GPSConvertManager.RadiansToDegrees(Math.Atan(elevationDifferenceMeters / onEarthDistance)));
            }
            else
            {
                _elevation = new Elevation();

                //fire an event?  no elevation information to calculate the direction
                //so it can be handled by a custom function and calculated by an override way

            }
        }


        #endregion

        #region methods

        private void evalueDistanceFromTwoPoints( Position PositionStart, Position PositionEnd )
        {


            //// compute the course
            //course = Math.Atan2(distanceEast, distanceNorth) % (2 * Math.PI);
            //course = RadiansToDegrees(course);
            //if (course < 0)
            //    course += 360;
        
        
        }

        #endregion

        #region properties

        /// <summary>
        /// Azimuth
        /// </summary>
        internal Azimuth Azimuth
        {
            get { return _azimuth; }
        }

        /// <summary>
        /// Elevation
        /// </summary>
        internal Elevation Elevation
        {
            get { return _elevation; }
        }

        #endregion

        #region IEquatable<Direction> Members

        /// <summary>
        /// Check both Azimuth and Elevetion
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool Equals(Direction other)
        {
           if ( ( this._azimuth.Equals( other._azimuth ) ) & ( this._elevation .Equals( other._elevation ) ) )
               return true;
           else
               return false;            
        }

        #endregion

        #region operators
                
        /// <summary>
        /// Sum two Direction Object (fully Azimuth and Elevation.
        /// </summary>
        /// <param name="Direction1"></param>
        /// <param name="Direction2"></param>
        /// <returns></returns>
        public static Direction operator +(Direction Direction1, Direction Direction2)
        {
            Direction temp = new Direction();

            temp._azimuth.Add(new Angle(Direction1._azimuth.DecimalDegrees));
            temp._elevation.Add(new Angle(Direction1._elevation.DecimalDegrees));

            temp._azimuth.Add(new Angle(Direction2._azimuth.DecimalDegrees));
            temp._elevation.Add(new Angle(Direction2._elevation.DecimalDegrees));

            return temp;
        }



        /// <summary>
        /// Subtract two Direction Object (fully Azimuth and Elevation.
        /// </summary>
        /// <param name="Direction"></param>
        /// <returns></returns>
        public static Direction operator -(Direction Direction)
        {
            Direction temp = new Direction();
            
            temp._azimuth = new Azimuth( Direction._azimuth.DecimalDegrees + 180.0 );
            temp._elevation = new Elevation( - Direction._elevation.DecimalDegrees );

            return temp;
        }

        #endregion
    }
}